package Pattern;

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import Basics.Atom;
import Basics.Program;
import Basics.Rule;
import Derivation.DeriveByRuleTopDown;

public class IntersectWithProgram {

	Pattern pattern;

	Program p;



	public IntersectWithProgram () {}
	
	
	
	public IntersectWithProgram (PatternNode ipattern)
	{
		this.pattern = new Pattern(ipattern);
	}
	
	
	
	public IntersectWithProgram (Vector<Vector<PatternNode>> patternVec)
	{
		this.pattern = new Pattern(patternVec);
	}
	
	
	
	public IntersectWithProgram (Program ip, Pattern ipattern)
	{
		this.p = ip;
		this.pattern = ipattern;
	}
	
	
	
	public IntersectWithProgram (Program ip, Vector<Vector<PatternNode>> patternVec)
	{
		this.p = ip;
		this.pattern = new Pattern(patternVec);
	}

	

	public Program getP() 
	{
		return p;
	}



	public void setP(Program p) 
	{
		this.p = p;
	}
	
	
	
	public Pattern getPattern() 
	{
		return pattern;
	}



	public void setPattern(Pattern pattern) 
	{
		this.pattern = pattern;
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: NoTransitives																				
	/** Description: Intersect pattern With Program no transitive children  			
	/*************************************************************************************************************/
	
	public void IntersectNoTransitives ()
	{
		this.pattern.Renaming();
		for (Vector<PatternNode> nodeVec : this.pattern.getPatternVec()) 
		{
			if (false == this.pattern.patternVec.lastElement().equals(nodeVec)) 
			{
				for (PatternNode patternNode : nodeVec) 
				{
					Vector<Rule> addedRules = new Vector<Rule> (); 
					for (Rule rule : this.p.getRules()) 
					{
						if (true == rule.getHead().FittsPartialInst(patternNode)) 
						{
							addedRules.addAll(FindPartialInstRules(patternNode, rule));
						}
					}
					
					this.p.addRules(addedRules);
				}
			}
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: FindPartialInstRules																				
	/** Description: use in pattern to find partial inst that satisfies children in pattern  			
	/*************************************************************************************************************/
	
	public Vector<Rule> FindPartialInstRules (PatternNode headPatternNode, Rule r)
	{
		Vector<Rule> derivVec = new Vector<Rule>();
		Rule copyOfRule = new Rule(r);
		
		//find all combinations of rule according to pattern
		DeriveByRuleTopDown dr = new DeriveByRuleTopDown(copyOfRule);
		copyOfRule.getHead().setName(headPatternNode.getNewName());
		copyOfRule.SwapToInstAtomAndPartiallyInst(copyOfRule.getHead(), headPatternNode);
		dr.FindDerivationsForRuleTopDownForIntersection(DefineChildrensDB(headPatternNode)); //top-down
		Vector<Atom> children = ConvertPatternNodeVecToAtomVec(headPatternNode.getChildren());
		
		for (Rule partlyInstRule : dr.getDerivations()) 
		{
			if (partlyInstRule.CotainedInBody(children))
			{
				for (PatternNode patternChild : headPatternNode.getChildren()) 
				{
					for (Atom bodyAtom : partlyInstRule.getBody()) 
					{
						if (patternChild.equals(bodyAtom)) 
						{
							bodyAtom.setName(patternChild.getNewName());
						}
					}
					
					partlyInstRule.getHead().setName(headPatternNode.getNewName());
					derivVec.add(partlyInstRule);
				}		
			}
		}
		
		return derivVec;
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: ConvertPatternNode2VecToAtomVec																				
	/** Description: use in pattern to help convert pattern children to atoms  			
	/*************************************************************************************************************/
	
	public Vector<Atom> ConvertPatternNodeVecToAtomVec (Vector<PatternNode> patternChildren)
	{
		Vector<Atom> children = new Vector<Atom>(); 
		for (PatternNode child : patternChildren) 
		{
			children.add(child);
		}
		
		return children;
	}
	
	
    
    /*************************************************************************************************************/
	/** Title: DefineChildrensDB																				
	/** Description: create a pattern based DB to find inst with  			
	/*************************************************************************************************************/
    
	public Map<String, Vector<Atom>> DefineChildrensDB (PatternNode node)
	{
		Map<String, Vector<Atom>> childrenDB = new HashMap<String, Vector<Atom>>();
		for (PatternNode child : node.getChildren()) 
		{
			Update(child, childrenDB);
		}
		
		return childrenDB;
	}
	
	
	
	
	/*************************************************************************************************************/
    /** Title: Update                                                                                                                                                               
    /** Description: add new fact to DB                     
    /*************************************************************************************************************/
    
    public void Update (Atom atom, Map<String, Vector<Atom>> childrenDB)
    {
    	
    	if (null == childrenDB.get(atom.getName()))
    	{
    		childrenDB.put(atom.getName(), new Vector<Atom>());
    	}

    	childrenDB.get(atom.getName()).add(atom);
    }
}

